#include "legoland.h"
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <string.h>
#include <cmath>

/*****************************************************************
* LEGOLAND - Data structure that holds genus/species information
*   and associated k-means cluster probabilities
*
* Created by: Bryan Quach
* Date Created: 10 April 2012
* Last Modified: 3 May 2012
******************************************************************/

using namespace std;

/*************************************************************************
* legoland - constructor
*   sets up variables to default values
**************************************************************************/
legoland::legoland()
{
    multiplier = 17000000000; //arbitrarily assign centroid coordinate multiplier

    /*Initialize legoville*/
    legoville = new vector<lego>[(multiplier+1)*(multiplier+1)];
}

/*************************************************************************
* legoland - constructor
*   parameters:
*       -index_factor: multiplier value
*   sets up variables based upon user specified multiplier value
**************************************************************************/
legoland::legoland(unsigned long long index_factor)
{
    multiplier = index_factor;

    /*Initialize legoville*/
    legoville = new vector<lego>[(multiplier+1)*(multiplier+1)];
}

/*************************************************************************
* get_size_legoville
*   Returns to user the size (number of elements) in legoville
**************************************************************************/
unsigned long long legoland::get_size_legoville()
{
    return (multiplier+1)*(multiplier+1);
}

/*************************************************************************
* legoland - deconstructor
*   delete dynamically allocated memory
**************************************************************************/
legoland::~legoland()
{
    delete legoville;
}

/*************************************************************************
* add - add elements to legoville
*   paramters:
*       -taxon: genus/species name
*       -centroids: vector holding centroid coordinates and probabilities
*   return: true if add successful, false if otherwise
**************************************************************************/
bool legoland::add(unsigned short taxon, centroid centroids[], int numCentroids)
{
    lego l;
    l.taxon = taxon;
    unsigned long long x;
    unsigned long long y;

    /**Add to legoville**/
    for(int i = 0; i < numCentroids; i++)
    {
        centroid cluster = centroids[i];
        x = floor(cluster.x*multiplier+0.5); //convert from float to int
        y = floor(cluster.y*multiplier+0.5); //convert from float to int
        y = y*multiplier+y; //calculate "row" position for legoville array
        x = x + y;

        l.prob = cluster.prob;

        /***Check if taxon already exists at the given position***/
        bool duplicate = false;
        if(legoville[x].size() > 0)
        {
            for(int j = 0; j < legoville[x].size(); j++)
            {
                if(legoville[x].at(j).taxon == l.taxon)
                {
                    legoville[x].at(j).prob += l.prob; //update existing lego
                    duplicate = true;
                    break;
                }
            }
        }
        if(duplicate == false)
        {
            legoville[x].push_back(l); //add lego to legoville
        }
    }
    return true;
}

/*****************************************************************************************
* load_legos - adds a vector of legos to a specific index in legoville
*   parameters:
*       -index_factor: index in legoville
*       -v: vector of legos (genus/species code and probabilities) at the coordinate given
*   returns true if loaded else false (if out of range)
******************************************************************************************/
bool legoland::load_legos(unsigned long long index_factor, vector<lego> v)
{
    if(index_factor>((multiplier+1)*(multiplier+1)))
        return false;
    legoville[index_factor]=v;
    return true;
}

/*****************************************************************************************
* search - find lego within legoville using centroid coordinates
*   parameters:
*       -c: coordinates for a particular DNA read
*   return: vector of legos (genus/species code and probabilities) at the coordinate given
******************************************************************************************/
vector<lego> legoland::search(coordinates c)
{
    unsigned long long int x = floor(c.x*multiplier+0.5);
    unsigned long long int y = floor(c.x*multiplier+0.5);
    y = y * multiplier + y;  //calculate "row" position for legoville array
    x = x + y;
    return legoville[x];
}//end search


/********************************************************************************************
* search - find lego within legoville using an index position between 0 and size of legoville
*   parameters:
*       -lego_index: index position of lego vector in legoville
*   return: vector of legos (genus/species code and probabilities)
*********************************************************************************************/
vector<lego> legoland::search(unsigned long long lego_index)
{
    return legoville[lego_index];
}//end search

/*****************************************************************************************
* get_multiplier - accessor method to get the multiplier
*   parameters:
*       none
*   return: multiplier of type unsigned long long
******************************************************************************************/
unsigned long long legoland::get_multiplier()
{
    return multiplier;
}

/*****************************************************************************************
* change_multiplier - update the multiplier
*   parameters:
*       none
*   return: n/a
******************************************************************************************/
void legoland::change_multiplier(unsigned long long m)
{
    multiplier = m;
}
